package com.admin.system.modules.oauth.rest;

import cn.hutool.core.util.IdUtil;
import com.admin.common.constant.Constant;
import com.admin.common.enums.LoginCodeEnum;
import com.admin.common.exception.BadRequestException;
import com.admin.common.utils.*;
import com.admin.component.redis.RedisUtils;
import com.admin.component.web.RequestUtil;
import com.admin.core.dto.cache.ParamConfigCacheBO;
import com.admin.core.entity.SysUser;
import com.admin.core.mapper.UserMapper;
import com.admin.logging.annotation.Log;
import com.admin.logging.enums.OperateTypeEnum;
import com.admin.oauth.annotation.AnonymousAccess;
import com.admin.oauth.component.UserCacheManager;
import com.admin.oauth.constant.CacheConstant;
import com.admin.oauth.dto.AuthUserDto;
import com.admin.oauth.dto.LoginUserDto;
import com.admin.oauth.dto.OnlineUserDto;
import com.admin.oauth.util.LoginUserUtil;
import com.admin.system.modules.system.service.UserService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author sq-lan
 * 登录、获取用户信息、退出登录、验证码
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Api(tags = "系统：系统授权接口")
public class AuthorizationController {

    private final RedisUtils redisUtils;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    private final UserCacheManager userCacheManager;
    private final UserService userService;
    private final UserMapper userMapper;

    @AnonymousAccess
    @GetMapping(value = "/test")
    public ResponseEntity<Object> test() {
        // return ResponseEntity.ok(userMapper.update());
        // return ResponseEntity.ok(userMapper.insert(new SysUser()));
        // return ResponseEntity.ok(userMapper.deleteById(99));
        // return ResponseEntity.ok(userMapper.test(new TestDataScope("小明"), "a", 2, new SysRole()));
        return ResponseEntity.ok(userService.list(Wrappers.<SysUser>lambdaQuery().like(SysUser::getNickName, "测试")));
    }

    @Log(module = "登录", operateType = OperateTypeEnum.LOGIN)
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping("/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaUtils.RSA_PRIVATE_KEY, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = TokenUtil.generate(authentication.getName());
        final LoginUserDto loginUserDto = (LoginUserDto) authentication.getPrincipal();
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(8) {{
            put("token", Constant.JWT_TOKEN_START_WITH + token);
            put("user", loginUserDto);
        }};
        if (ParamConfigCacheBO.loginParamConfig.getSingleLogin()) {
            // 踢掉之前已经登录的
            userCacheManager.kickOut(authUser.getUsername());
        }

        // 添加缓存数据
        userCacheManager.set(token, loginUserDto, createOnlineUserDto(loginUserDto, token, request));
        // 返回登录信息
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<UserDetails> getUserInfo() {
        return ResponseEntity.ok(LoginUserUtil.jwtUserDto());
    }

    @ApiOperation("获取验证码")
    @AnonymousAccess
    @GetMapping("/code")
    public ResponseEntity<Object> getCode() {
        // 获取运算的结果
        Captcha captcha = CaptchaUtil.create(ParamConfigCacheBO.captchaParamConfig);
        String uuid = CacheConstant.CACHE_KEY_CAPTCHA + IdUtil.simpleUUID();
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        String idx = ".";
        if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(idx)) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisUtils.set(uuid, captchaValue, ParamConfigCacheBO.captchaParamConfig.getExpiration());
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);
    }

    @ApiOperation("退出登录")
    @AnonymousAccess
    @DeleteMapping("/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        String token = TokenUtil.get(request.getHeader(Constant.JWT_HEADER));
        userCacheManager.logout(token);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    private OnlineUserDto createOnlineUserDto(LoginUserDto loginUserDto, String token, HttpServletRequest request) {
        String ip = null;
        String browser = null;
        String address = null;
        String encryptToken = null;
        try {
            ip = RequestUtil.getIp(request);
            browser = RequestUtil.getBrowser(request);
            address = IpUtil.getCityInfo(ip);
            encryptToken = EncryptUtils.desEncrypt(token);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
        }
        return new OnlineUserDto(loginUserDto.getUsername(), loginUserDto.getUser().getNickName(),
                loginUserDto.getUser().getDept().getName(), browser, ip, address, encryptToken, new Date());
    }
}
